package com.iflytek.edu.db.core.parse;

import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLObject;
import com.alibaba.druid.sql.ast.SQLOrderBy;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr;
import com.alibaba.druid.sql.ast.expr.SQLCharExpr;
import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr;
import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr;
import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr;
import com.alibaba.druid.sql.ast.statement.SQLDeleteStatement;
import com.alibaba.druid.sql.ast.statement.SQLExprTableSource;
import com.alibaba.druid.sql.ast.statement.SQLInsertStatement;
import com.alibaba.druid.sql.ast.statement.SQLJoinTableSource;
import com.alibaba.druid.sql.ast.statement.SQLSelect;
import com.alibaba.druid.sql.ast.statement.SQLSelectGroupByClause;
import com.alibaba.druid.sql.ast.statement.SQLSelectItem;
import com.alibaba.druid.sql.ast.statement.SQLSelectOrderByItem;
import com.alibaba.druid.sql.ast.statement.SQLSelectQuery;
import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock;
import com.alibaba.druid.sql.ast.statement.SQLSelectStatement;
import com.alibaba.druid.sql.ast.statement.SQLTableSource;
import com.alibaba.druid.sql.ast.statement.SQLUnionQuery;
import com.alibaba.druid.sql.ast.statement.SQLUpdateSetItem;
import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement;
import com.iflytek.edu.db.core.context.AdapterContext;
import com.iflytek.edu.db.core.strategy.Strategy;
import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.Optional;

/**
 * @author linkai
 * @date 2022/1/12
 */
@Slf4j
public class SqlParser {

    private final SqlAdapterConfig adapterConfig;

    public SqlParser(SqlAdapterConfig adapterConfig) {
        this.adapterConfig = adapterConfig;
    }

    /**
     * statement 解析
     *
     * @param sqlStatement AST语法树
     */
    public void parse(SQLStatement sqlStatement) {
        if (sqlStatement instanceof SQLSelectStatement) {
            parseSelect((SQLSelectStatement) sqlStatement);
        } else if (sqlStatement instanceof SQLInsertStatement) {
            parseInsert((SQLInsertStatement) sqlStatement);
        } else if (sqlStatement instanceof SQLDeleteStatement) {
            parseDelete((SQLDeleteStatement) sqlStatement);
        } else if (sqlStatement instanceof SQLUpdateStatement) {
            parseUpdate((SQLUpdateStatement) sqlStatement);
        } else {
            log.info("无须处理表达式：class = {}, expr = {}", sqlStatement.getClass(), sqlStatement);
        }
    }

    /**
     * select 解析
     *
     * @param sqlStatement AST语法树
     */
    public void parseSelect(SQLSelectStatement sqlStatement) {
        SQLSelect select = sqlStatement.getSelect();
        Optional.ofNullable(select).ifPresent(v -> parseQuery(v.getQuery()));
    }

    /**
     * update 更新
     *
     * @param sqlStatement AST语法树
     */
    public void parseUpdate(SQLUpdateStatement sqlStatement) {
        Optional.ofNullable(sqlStatement.getFrom()).ifPresent(this::parseTableSource);
        Optional.ofNullable(sqlStatement.getTableSource()).ifPresent(this::parseTableSource);
        Optional.ofNullable(sqlStatement.getItems()).ifPresent(v -> v.forEach(this::parseUpdateSetItem));
        Optional.ofNullable(sqlStatement.getWhere()).ifPresent(this::parseExpr);
//        Optional.ofNullable(sqlStatement.getOrderBy()).ifPresent(this::parseOrderBy);
    }

    /**
     * insert 插入
     *
     * @param sqlStatement AST语法树
     */
    public void parseInsert(SQLInsertStatement sqlStatement) {
        Optional.ofNullable(sqlStatement.getTableSource()).ifPresent(this::parseTableSource);
        Optional.ofNullable(sqlStatement.getColumns()).ifPresent(v -> v.forEach(this::parseExpr));
        Optional.ofNullable(sqlStatement.getValuesList()).ifPresent(v -> v.forEach(this::parseValuesClause));
    }

    /**
     * delete 删除
     *
     * @param sqlStatement AST语法树
     */
    public void parseDelete(SQLDeleteStatement sqlStatement) {
        Optional.ofNullable(sqlStatement.getTableSource()).ifPresent(this::parseTableSource);
        Optional.ofNullable(sqlStatement.getWhere()).ifPresent(this::parseExpr);
    }

    public void parseUpdateSetItem(SQLUpdateSetItem item) {
        Optional.ofNullable(item.getColumn()).ifPresent(this::parseExpr);
        Optional.ofNullable(item.getValue()).ifPresent(this::parseExpr);
    }

    public void parseQuery(SQLSelectQuery query) {
        // 查询
        if (query instanceof SQLSelectQueryBlock) {
            SQLSelectQueryBlock queryObject = (SQLSelectQueryBlock) query;
            List<SQLSelectItem> selectList = queryObject.getSelectList();
            Optional.ofNullable(selectList).ifPresent(
                    v -> v.forEach(item -> parseExpr(item.getExpr()))
            );
            Optional.ofNullable(queryObject.getFrom()).ifPresent(this::parseTableSource);
            Optional.ofNullable(queryObject.getWhere()).ifPresent(this::parseExpr);
            Optional.ofNullable(queryObject.getGroupBy()).ifPresent(this::parseGroupBy);
            Optional.ofNullable(queryObject.getOrderBy()).ifPresent(this::parseOrderBy);

        } else if (query instanceof SQLUnionQuery) {
            SQLUnionQuery queryObject = (SQLUnionQuery) query;
            Optional.ofNullable(queryObject.getLeft()).ifPresent(this::parseQuery);
            Optional.ofNullable(queryObject.getRight()).ifPresent(this::parseQuery);
            Optional.ofNullable(queryObject.getOrderBy()).ifPresent(this::parseOrderBy);
        } else {
            log.info("无须处理表达式：class = {}, expr = {}", query.getClass(), query);
        }
    }

    public void parseTableSource(SQLTableSource sql) {
        if (sql instanceof SQLJoinTableSource) {
            SQLJoinTableSource table = (SQLJoinTableSource) sql;
            Optional.ofNullable(table.getLeft()).ifPresent(this::parseTableSource);
            Optional.ofNullable(table.getRight()).ifPresent(this::parseTableSource);
            Optional.ofNullable(table.getCondition()).ifPresent(this::parseExpr);
            // 语法适配
            grammarStrategy(table);

        } else if (sql instanceof SQLExprTableSource) {
            SQLExprTableSource table = (SQLExprTableSource) sql;
            Optional.ofNullable(table.getExpr()).ifPresent(this::parseExpr);
            // 语法适配
            grammarStrategy(table);
        }
    }

    public void parseGroupBy(SQLSelectGroupByClause group) {
        Optional.ofNullable(group.getItems())
                .ifPresent(v -> v.forEach(this::parseExpr));
    }

    public void parseOrderBy(SQLOrderBy order) {
        Optional.ofNullable(order.getItems())
                .ifPresent(v -> v.forEach(this::parseOrderByItem));
    }

    public void parseOrderByItem(SQLSelectOrderByItem item) {
        Optional.ofNullable(item.getExpr())
                .ifPresent(this::parseExpr);
    }

    public void parseValuesClause(SQLInsertStatement.ValuesClause value) {
        Optional.ofNullable(value.getValues())
                .ifPresent(v -> v.forEach(this::parseExpr));
    }

    public void parseExpr(SQLExpr expr) {
        if (expr == null) {
            return;
        }
        // 函数
        if (expr instanceof SQLMethodInvokeExpr) {
            SQLMethodInvokeExpr expr1 = (SQLMethodInvokeExpr) expr;
            // 函数适配
            functionStrategy(expr1);
            // 函数参数列表
            Optional.ofNullable(expr1.getArguments()).ifPresent(
                    v -> v.forEach(this::parseExpr)
            );

        } else if (expr instanceof SQLPropertyExpr) {
            // 参数 a.table_name
            SQLPropertyExpr expr1 = (SQLPropertyExpr) expr;
            // 语法适配
            grammarStrategy(expr1);

        } else if (expr instanceof SQLIdentifierExpr) {
            // 标识 increment_sync
            SQLIdentifierExpr expr1 = (SQLIdentifierExpr) expr;

            // 语法适配
            grammarStrategy(expr1);

        } else if (expr instanceof SQLBinaryOpExpr) {
            // 二进制  b.filter = 1
            SQLBinaryOpExpr expr1 = (SQLBinaryOpExpr) expr;

            Optional.ofNullable(expr1.getLeft()).ifPresent(this::parseExpr);
            Optional.ofNullable(expr1.getRight()).ifPresent(this::parseExpr);

        } else if (expr instanceof SQLCharExpr) {
            // 字符 [
            SQLCharExpr expr1 = (SQLCharExpr) expr;

        } else if (expr instanceof SQLIntegerExpr) {
            // 数值 1
            SQLIntegerExpr expr1 = (SQLIntegerExpr) expr;

        } else {
            log.info("无须处理表达式：class = {}, expr = {}", expr.getClass(), expr);
        }
    }

    public void grammarStrategy(SQLObject sqlObj) {
        adapterConfig.getGrammarStrategyList().forEach(v -> {
            strategy(v, sqlObj);
        });
    }

    public void functionStrategy(SQLObject sqlObj) {
        adapterConfig.getFuncStrategyList().forEach(v -> {
            strategy(v, sqlObj);
        });
    }

    public void strategy(Strategy strategy, SQLObject sqlObj) {
        if (strategy.support(sqlObj)) {
            if (AdapterContext.isSearch()) {
                strategy.search(sqlObj);
            } else {
                strategy.convert(sqlObj);
            }
        }
    }
}
